package vn.bang.xiangqi.components;

import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import vn.bang.xiangqi.infrastructure.Position;
import vn.bang.xiangqi.infrastructure.Move;
import vn.bang.xiangqi.infrastructure.Vector;
import vn.bang.xiangqi.utility.BoardUtil;

public class Board {
	private List<Map<Integer, INode>> competitors = new ArrayList<Map<Integer, INode>>();
	private boolean arranged = false;
	private int turn = Competitor.RED.getIndex();
	private Move[] moves = new Move[BoardUtil.MAX_LOG_RECORDS];
	private int moveIndex = 0;

	public Board() {
		initialize();
	}

	private void initialize() {
		initialize(competitors.get(Competitor.BLACK.getIndex()));
		initialize(competitors.get(Competitor.RED.getIndex()));
		Arrange();
	}

	private void initialize(Map<Integer, INode> dictionary) {
		if (null != dictionary) {
			return;
		}

		dictionary = new Hashtable<Integer, INode>();
		dictionary.put(NodeKind.KING.getIndex(), new King()); // 1
		dictionary.put(NodeKind.ADVISOR.getIndex(), new Advisor()); // 2
		dictionary.put(NodeKind.ADVISOR.getIndex() + 1, new Advisor()); // 3
		dictionary.put(NodeKind.ELEPHANT.getIndex(), new Elephant()); // 4
		dictionary.put(NodeKind.ELEPHANT.getIndex() + 1, new Elephant()); // 5
		dictionary.put(NodeKind.CHARIOT.getIndex(), new Chariot()); // 6
		dictionary.put(NodeKind.CHARIOT.getIndex() + 1, new Chariot()); // 7
		dictionary.put(NodeKind.CANNON.getIndex(), new Cannon()); // 8
		dictionary.put(NodeKind.CANNON.getIndex() + 1, new Cannon()); // 9
		dictionary.put(NodeKind.HORSE.getIndex(), new Horse()); // 10
		dictionary.put(NodeKind.HORSE.getIndex() + 1, new Horse()); // 11
		dictionary.put(NodeKind.PAWN.getIndex(), new Pawn()); // 12
		dictionary.put(NodeKind.PAWN.getIndex() + 1, new Pawn()); // 13
		dictionary.put(NodeKind.PAWN.getIndex() + 2, new Pawn()); // 14
		dictionary.put(NodeKind.PAWN.getIndex() + 3, new Pawn()); // 15
		dictionary.put(NodeKind.PAWN.getIndex() + 4, new Pawn()); // 16
	}

	public void Arrange() {
		if (null == competitors.get(Competitor.RED.getIndex())
				|| null == competitors.get(Competitor.BLACK.getIndex())
				|| null == RED_ORIGINAL_POSITION
				|| null == BLACK_ORIGINAL_POSITION) {
			arranged = false;
			return;
		}

		arranged = true;
		arrange(competitors.get(Competitor.RED.getIndex()),
				RED_ORIGINAL_POSITION);
		arrange(competitors.get(Competitor.BLACK.getIndex()),
				BLACK_ORIGINAL_POSITION);
		turn = Competitor.RED.getIndex();
	}

	private void arrange(Map<Integer, INode> dictionary,
			Position[] originalPosition) {
		if (arranged) {
			return;
		}

		INode node = null;

		for (int nodeIndex = 0; nodeIndex < dictionary.size(); nodeIndex++) {
			node = dictionary.get(nodeIndex);
			if (null == node) {
				arranged = false;
				return;
			}
			arranged = true;
			node.GetCurrentPosition().setRowIndex(
					originalPosition[nodeIndex].getRowIndex());
			node.GetCurrentPosition().setColumnIndex(
					originalPosition[nodeIndex].getColumnIndex());

		}
	}

	public void Move(NodeKind nodeKind, int order, Position movingToPosition) {
		INode node = BoardUtil.GetNodeInList(nodeKind, order,
				competitors.get(turn));
		Move(node, movingToPosition);
	}

	public void Move(INode node, Position movingToPosition) {
		if (node.IsAlive()) {
			Position movingFromPosition = node.GetCurrentPosition();
			if (node.MoveTo(movingToPosition, competitors.get(turn).values(),
					competitors.get(1 - turn).values())) {
				moves[moveIndex++] = new Move(node, new Vector(
						movingFromPosition, movingToPosition)); // Write move
																// log.
				switchTurn();
			}
		}
	}

	public void MoveBack() {
		switchTurn();
		Move move = moves[--moveIndex];
		Position backPosition = move.getNode().GetCurrentPosition()
				.Add(move.getMovingVector().Negative());
		Move(move.getNode(), backPosition);
	}

	private void switchTurn() {
		turn = 1 - turn; // switch the turn.
		switchPosition();
	}

	private void switchPosition() {
		INode node = null;
		for (int turn = 0; turn < BoardUtil.NUMBER_OF_PLAYERS; turn++) {
			Map<Integer, INode> playerNodes = competitors.get(turn);
			for (int nodeIndex = 0; nodeIndex < playerNodes.size(); nodeIndex++) {
				node = playerNodes.get(nodeIndex);
				if (null != node && node.IsAlive()) {
					node.GetCurrentPosition().setRowIndex(
							BoardUtil.ROW_INDEX_MAX
									- node.GetCurrentPosition().getRowIndex()
									+ 1);
					node.GetCurrentPosition().setColumnIndex(
							BoardUtil.COLUMN_INDEX_MAX
									- node.GetCurrentPosition()
											.getColumnIndex() + 1);
				}
			}
		}
	}

	static private Position[] RED_ORIGINAL_POSITION = { new Position(10, 5),
			new Position(10, 4), new Position(10, 6), new Position(10, 3),
			new Position(10, 7), new Position(10, 1), new Position(10, 9),
			new Position(8, 2), new Position(8, 8), new Position(10, 2),
			new Position(10, 8), new Position(7, 1), new Position(7, 3),
			new Position(7, 5), new Position(7, 7), new Position(7, 9), };

	static private Position[] BLACK_ORIGINAL_POSITION = { new Position(1, 5),
			new Position(1, 4), new Position(1, 6), new Position(1, 3),
			new Position(1, 7), new Position(1, 1), new Position(1, 9),
			new Position(3, 2), new Position(3, 8), new Position(1, 2),
			new Position(1, 8), new Position(4, 1), new Position(4, 3),
			new Position(4, 5), new Position(4, 7), new Position(4, 9), };

	private enum Competitor {
		BLACK(0), RED(1);

		private int index;

		private Competitor(int index) {
			this.index = index;
		}

		public int getIndex() {
			return index;
		}
	}

}